package com.whpost.service.tools;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

/**
 * AES加密
 *
 * @author Stone
 */
public class AESCBCUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(AESCBCUtil.class);
    private final static String CHARSET = "UTF-8";

    /**
     * 密钥256位
     */
    private final static String KEY = "64WoTBYP0ZzzHipkML82EismLIjplF65Stx7oxz8R7KRDpGt28craYnCQMKIPQoQnjsF8sCrHYivRoJgtkUO6emgyQQKDObQc876d8PaJX1Wd9hwtwOzxtlgQwjrxjUR7D2e7PLBXa0Ks5DoNf9upuDXpbhoHXDtdjMHb1mxlaHvjS268U7p4BcKXooiKEyzk31GvvYPk9lIYG0d9yT4s8eZNGXEsVqYzfvUWweBoJ7hBwUeeucVqmDKFCwKtp1G";

    private static final String ALGORITHM = "AES";
    private final static int KEY_LENGTH=128;

    /**
     * 加密/解密算法 / 工作模式 / 填充方式
     * ECB 模式本质上是一种较弱的加密模式，因为相同的明文块加密成相同的密码块。
     * CBC (密码块链)模式是超级模式 ，因此它没有此弱点。
     */
    //public static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5PADDING";
    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5PADDING";

    /**
     * 必须16位
     * 初始化向量IV不可以为32位，否则异常java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
     */
    private static final String IV = "zqxp55TPYzMUW5ZT";

    /**
     * AES加密
     *
     * @param content
     * @return
     */
    public String encryptAES(String content) {
        byte[] encryptResult = encrypt(content);
        // BASE64位加密
        String encryptResultStr = Base64.encodeBase64String(encryptResult);
        return encryptResultStr;
    }

    /**
     * AES加密
     *
     * @param content
     * @return
     */
    public String encryptAES(String content,String aeskey,String aesiv) {
        byte[] encryptResult = encrypt(content,aeskey,aesiv);
        // BASE64位加密
        String encryptResultStr = Base64.encodeBase64String(encryptResult);
        return encryptResultStr;
    }



    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @return
     */
    private byte[] encrypt(String content) {
        if (StringUtils.isEmpty(content)) {
            LOGGER.error("AES encryption params is null");
            return null;
        }
        return encrypt(content,KEY,IV);
    }

    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @return
     */
    private byte[] encrypt(String content,String aeskey,String aesiv) {

        try {
            SecretKeySpec key = getSecretKey(aeskey);
            //偏移量
            AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(aesiv.getBytes());

            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);// 创建密码器（利用默认转换模式(可能是弱 ECB 模式)）
            //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");//将 AES 密码用于 CBC 模式
            cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);// 初始化

            byte[] byteContent = content.getBytes(CHARSET);
            byte[] result = cipher.doFinal(byteContent);
            return result; // 加密
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密
     *
     * @param encryptResultStr
     * @return
     */
    public String decryptAES(String encryptResultStr) throws Exception {
        // BASE64位解密
        byte[] decryptFrom = Base64.decodeBase64(encryptResultStr);
        byte[] decryptResult = decrypt(decryptFrom);
        return new String(decryptResult, CHARSET);
    }

    /**
     * AES解密
     *
     * @param encryptResultStr
     * @return
     */
    public String decryptAES(String encryptResultStr,String aeskey,String aesiv) throws Exception {
        // BASE64位解密
        byte[] decryptFrom = Base64.decodeBase64(encryptResultStr);
        byte[] decryptResult = decrypt(decryptFrom,aeskey,aesiv);
        return new String(decryptResult, CHARSET);
    }



    /**
     * 解密
     *
     * @param content 待解密内容
     * @return
     */
    private byte[] decrypt(byte[] content) throws Exception {
        if (StringUtils.isEmpty(content.toString())) {
            LOGGER.error("AES decryption params is null");
            return null;
        }
        return decrypt(content,KEY,IV);
    }

    /**
     * 解密
     *
     * @param content 待解密内容
     * @return
     */
    private byte[] decrypt(byte[] content,String aeskey,String aesiv) throws Exception {
        SecretKeySpec key = getSecretKey(aeskey);
        //偏移量
        AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(aesiv.getBytes());

        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);// 创建密码器
        //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC");//将 AES 密码用于 CBC 模式
        cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);// 初始化
        byte[] result = cipher.doFinal(content);
        return result; // 加密
    }

    /**
     * 生成加密秘钥
     *
     * @return
     */
    private SecretKeySpec getSecretKey(final String password) {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        KeyGenerator kg = null;

        try {
            kg = KeyGenerator.getInstance(ALGORITHM);

            // 防止linux下 随机生成key
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(password.getBytes());
            kg.init(KEY_LENGTH, secureRandom);
            //AES 要求密钥长度为 128
            //kg.init(128, new SecureRandom(password.getBytes()));

            //生成一个密钥
            SecretKey secretKey = kg.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            return new SecretKeySpec(enCodeFormat, ALGORITHM);// 转换为AES专用密钥
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }

        return null;
    }
}
